﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace WebFabricator.org
{
    /// <summary>
    /// Implements a chain of responsibiliy pattern by paticipating as a node in a doubly linked list.
    /// In this capacity the Adorner can:
    /// •process a request and run the next adorner
    /// •process request partially and run the next adorner
    /// •ignore the request and pass the control to the next adorner
    /// •run next adorner and, after it returns, post process the request
    /// •simply return, without processing or passing control down the chain
    /// •throw an exception
    /// •manipulate the fabric
    /// It can also modify the invocation of descendant nodes that are not directly coupled to it (ie it's immedidate descendant) 
    /// </summary>
    /// <typeparam name="T"></typeparam>
    [Namespace(typeof(Adorner<>), AllowMultiple = true)]
    public abstract class Adorner<T> : IAdorner<T>
    {
        //The next Handler in the chain
        private Adorner<T> successor;
        //The previous Handler in the chain
        private Adorner<T> predecessor;

        public Adorner()
        {
        }

        public abstract void Initialize(T fabric);
      
        /// <summary>
        /// Sets the Next logger to make a list/chain of Handlers
        /// </summary>
        public virtual IAdorner<T> Next(Adorner<T> handler)
        {
            successor = handler;
            successor.predecessor = this;
            return handler;
        }

        public void Fabricate(IWebFabric<T> fabric)
        {
            NotifySuccessor().Invoke(fabric);
        }

        private Action<IWebFabric<T>> NotifySuccessor()
        {
            if (successor == null)
                return Execute(NotifyPredecessor(Adorn));
            else
                return Execute(NotifyPredecessor(Adorn), successor.NotifySuccessor());
        }

        private Action<IWebFabric<T>> NotifyPredecessor(Action<IWebFabric<T>> action)
        {
            if (predecessor == null)
                return Ornament(action);
            else
                return Ornament(predecessor.NotifyPredecessor(action));
        }

        protected internal virtual Action<IWebFabric<T>> Ornament(Action<IWebFabric<T>> action)
        {
            return action;
        }

        protected internal virtual Action<IWebFabric<T>> Execute(Action<IWebFabric<T>> action)
        {
            return new Action<IWebFabric<T>>(delegate(IWebFabric<T> fabric)
            {
                action(fabric);
            });
        }

        protected internal virtual Action<IWebFabric<T>> Execute(Action<IWebFabric<T>> pre, Action<IWebFabric<T>> post)
        {
            return new Action<IWebFabric<T>>(delegate(IWebFabric<T> fabric)
            {
                pre(fabric);
                post(fabric);
                //f(request);

            });
        }

        protected internal abstract void Adorn(IWebFabric<T> fabric);

        // Implement IDisposable. 
        // Do not make this method virtual. 
        // A derived class should not be able to override this method. 
        public void Dispose()
        {
            if (successor != null)
                successor.Dispose();
           
            Dispose(true);
            // This object will be cleaned up by the Dispose method. 
            // Therefore, you should call GC.SupressFinalize to 
            // take this object off the finalization queue 
            // and prevent finalization code for this object 
            // from executing a second time.
            GC.SuppressFinalize(this);
        }

        // Dispose(bool disposing) executes in two distinct scenarios. 
        // If disposing equals true, the method has been called directly 
        // or indirectly by a user's code. Managed and unmanaged resources 
        // can be disposed. 
        // If disposing equals false, the method has been called by the 
        // runtime from inside the finalizer and you should not reference 
        // other objects. Only unmanaged resources can be disposed. 
        protected internal abstract void Dispose(bool disposing);
      

    }
}
